﻿namespace WPForms.Phone.Services
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using WPForms.Common.Model;
    using WPForms.Common.Services;
    using WPForms.Phone.DataAccess;

    /// <summary>
    /// Form store implementation, which uses SQL CE to store all data.
    /// </summary>
    public class SqlCeFormStore : IFormStore
    {
        /// <summary>
        /// Reference to the dataaccess helper
        /// </summary>
        private IDataAccess dataAccess;

        /// <summary>
        /// Stores all instance descriptions.
        /// </summary>
        private FormDescriptionCollection descriptions;

        /// <summary>
        /// Initializes a new instance of the <see cref="SqlCeFormStore" /> class.
        /// </summary>
        /// <param name="dataAccess">The data access.</param>
        /// <param name="descriptions">The descriptions.</param>
        public SqlCeFormStore(IDataAccess dataAccess, FormDescriptionCollection descriptions)
        {
            this.dataAccess = dataAccess;
            this.descriptions = descriptions;
        }

        /// <summary>
        /// Creates a new form in the store.
        /// </summary>
        /// <param name="instanceDescription">The instance description.</param>
        /// <returns>The created instance.</returns>
        public Common.Model.FormInstance CreateForm(Common.Model.FormInstanceDescription instanceDescription)
        {
            var created = new Form()
            {
                Name = instanceDescription.InstanceName,
                ResourceUri = instanceDescription.Uri.ToString(),
            };

            this.dataAccess.InTransaction(
                dc =>
                {
                    dc.Forms.InsertOnSubmit(created);
                });

            var instance = WPForms.Phone.Model.FormXamlFactory.CreateFromXaml(instanceDescription.Uri);
            instance.Name = instanceDescription.InstanceName;
            instance.Id = created.Id;
            return instance;
        }

        /// <summary>
        /// Deletes the given form from the store.
        /// </summary>
        /// <param name="form">The form instance to delete.</param>
        public void DeleteForm(Common.Model.FormInstance form)
        {
            this.dataAccess.InTransaction(
                dc =>
                {
                    var formModel = dc.Forms.Single(f => f.Id == form.Id);
                    dc.FieldValues.DeleteAllOnSubmit(formModel.FieldValues);
                    dc.Attachments.DeleteAllOnSubmit(formModel.Attachments);
                    dc.Forms.DeleteOnSubmit(formModel);
                });
        }

        /// <summary>
        /// Returns the list of form instances present in the store.
        /// </summary>
        /// <returns>The list of forms</returns>
        public System.Collections.ObjectModel.ReadOnlyCollection<Common.Model.FormInstanceDescription> GetForms()
        {
            var formsFound = new List<FormInstanceDescription>();
            this.dataAccess.InSession(
                dc =>
                {
                    foreach (var form in dc.Forms)
                    {
                        var desc = this.descriptions.Single(d => d.Uri.ToString() == form.ResourceUri);
                        var instanceDescription = new FormInstanceDescription()
                        {
                            Description = desc.Description,
                            FormName = desc.FormName,
                            Uri = desc.Uri,
                            Id = form.Id,
                            InstanceName = form.Name,
                        };

                        formsFound.Add(instanceDescription);
                    }
                });

            return new System.Collections.ObjectModel.ReadOnlyCollection<FormInstanceDescription>(formsFound);
        }

        /// <summary>
        /// Loads a form identified by its instance description.
        /// </summary>
        /// <param name="description">The description.</param>
        /// <returns>The loaded form.</returns>
        public Common.Model.FormInstance LoadForm(Common.Model.FormInstanceDescription description)
        {
            return this.LoadForm(description.Id);
        }

        /// <summary>
        /// Updates the form in the store.
        /// </summary>
        /// <param name="form">The form instance to update.</param>
        public void UpdateForm(Common.Model.FormInstance form)
        {
            this.dataAccess.InTransaction(
                dc =>
                {
                    form.ForAllFields(
                        field =>
                        {
                            bool created = false;
                            var value = dc.FieldValues.Where(fv => fv.Key == field.Key && fv.FormId == form.Id).SingleOrDefault();
                            if (value == null)
                            {
                                created = true;
                                value = new FieldValue()
                                {
                                    FormId = form.Id,
                                    Key = field.Key,
                                };
                            }

                            var builder = new System.Text.StringBuilder();
                            field.Write(builder);

                            var newValue = builder.ToString();
                            if (value.Value != newValue)
                            {
                                value.Value = newValue;
                            }

                            if (created)
                            {
                                dc.FieldValues.InsertOnSubmit(value);
                            }
                        });
                    form.ForAllAttachments(
                        attachment =>
                        {
                            var value = dc.Attachments.Single(a => a.Id == attachment.Id);
                            value.Data = attachment.Data;
                        });
                });
        }

        /// <summary>
        /// Loads a form using its id.
        /// </summary>
        /// <param name="formId">The id of the form to load.</param>
        /// <returns>The loaded form.</returns>
        public Common.Model.FormInstance LoadForm(int formId)
        {
            FormInstance loadedForm = null;
            this.dataAccess.InSession(
                dc =>
                {
                    var form = dc.Forms.Where(f => f.Id == formId).Single();
                    loadedForm = Model.FormXamlFactory.CreateFromXaml(new Uri(form.ResourceUri, UriKind.RelativeOrAbsolute));
                    loadedForm.Id = formId;
                    loadedForm.Name = form.Name;
                    foreach (var value in form.FieldValues)
                    {
                        var element = loadedForm.FindElementByKey<FormField>(value.Key);
                        if (element != null)
                        {
                            element.Read(value.Value);
                        }
                    }

                    foreach (var attachment in form.Attachments)
                    {
                        var model = new WPForms.Common.Model.Attachment()
                        {
                            Data = attachment.Data,
                            Id = attachment.Id,
                        };

                        if (attachment.FormSection != null)
                        {
                            var section = loadedForm.FindElementByKey<FormSection>(attachment.FormSection);
                            section.Attachments.Add(model);
                        }
                        else
                        {
                            loadedForm.MainSection.Attachments.Add(model);
                        }
                    }
                });

            return loadedForm;
        }
    }
}
